package com.netintech.project.system.rtsp;

import com.netintech.common.utils.Md5Utils;
import com.netintech.project.system.ffmpeg.aaa;
import com.netintech.project.system.ffmpeg.bbb;
import com.netintech.project.system.jna.CLibrary;
import com.netintech.project.system.rtsp.udp.UDPReceiver;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 管理rtsp连接
 */
public class RTSPClient extends Thread implements IEvent {

    private static final String VERSION = " RTSP/1.0 \r\n";
    private static final String RTSP_OK = "RTSP/1.0 200 OK";
    private static final String RTSP_UNAUTHORIZED = "RTSP/1.0 401 Unauthorized";
    private static final String USER_AGENT = "User-Agent: LibVLC/3.0.5 (LIVE555 Streaming Media v2016.11.28)";

    /**
     * 远程地址
     */
    private final InetSocketAddress remoteAddress;

    /**
     * 本地地址
     */
    private final InetSocketAddress localAddress;

    private final String userName;

    private final String password;

    private String authorization;

    /**
     * 连接通道
     */
    private SocketChannel socketChannel;

    /**
     * 发送缓冲区
     */
    private final ByteBuffer sendBuf;

    /**
     * 接收缓冲区
     */
    private final ByteBuffer receiveBuf;

    private static final int BUFFER_SIZE = 8192;

    /**
     * 端口选择器
     */
    private Selector selector;

    private String address;

    private Status sysStatus;

    private String sessionid;

    /**
     * 多线程是否结束的标志
     */
    private volatile boolean shutdown;

    public boolean getShutdown() {
        return this.shutdown;
    }


    private int seq = 1;

    /**
     * 是否发送过消息，当处理过接收的信息后，置为false，保证处理完上一次发送的返回信息后再发送
     */
    private boolean isSended;


    private String trackInfo;


    private enum Status {
        init, options, describe, setup, play, pause, teardown
    }

    public RTSPClient(InetSocketAddress remoteAddress,
                      InetSocketAddress localAddress, String address, String userName, String password) {
        this.remoteAddress = remoteAddress;
        this.localAddress = localAddress;
        this.address = address;
        this.userName = userName;
        this.password = password;

        // 初始化缓冲区  
        sendBuf = ByteBuffer.allocateDirect(BUFFER_SIZE);
        receiveBuf = ByteBuffer.allocateDirect(BUFFER_SIZE);
        if (selector == null) {
            // 创建新的Selector  
            try {
                selector = Selector.open();
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }

        startup();
        sysStatus = Status.init;
        shutdown = false;
        isSended = false;
    }

    /**
     * 建立连接通道
     */
    public void startup() {
        try {
            // 打开通道  
            socketChannel = SocketChannel.open();
            // 绑定到本地端口  
            socketChannel.socket().setSoTimeout(30000);
            socketChannel.configureBlocking(false);
            socketChannel.socket().bind(localAddress);
            boolean connect = socketChannel.connect(remoteAddress);
            if (connect) {
                System.out.println("开始建立连接:" + remoteAddress);
            }
            socketChannel.register(selector, SelectionKey.OP_CONNECT
                    | SelectionKey.OP_READ | SelectionKey.OP_WRITE, this);
            System.out.println("端口打开成功");

        } catch (final IOException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 发送信息到服务端
     *
     * @param out
     */
    public void send(byte[] out) {
        if (out == null || out.length < 1) {
            return;
        }
        synchronized (sendBuf) {
            sendBuf.clear();
            sendBuf.put(out);
            sendBuf.flip();
        }

        // 发送出去  
        try {
            write();
            isSended = true;
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送
     *
     * @throws IOException
     */
    public void write() throws IOException {
        if (isConnected()) {
            try {
                socketChannel.write(sendBuf);
            } catch (final IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("通道为空或者没有连接上");
        }
    }

    /**
     * 接收通道传过来的字节数组
     *
     * @return
     */
    public byte[] recieve() {
        if (isConnected()) {
            try {
                int len = 0;
                int readBytes = 0;

                synchronized (receiveBuf) {
                    receiveBuf.clear();
                    try {
                        while ((len = socketChannel.read(receiveBuf)) > 0) {
                            readBytes += len;
                        }
                    } finally {
                        receiveBuf.flip();
                    }
                    if (readBytes > 0) {
                        final byte[] tmp = new byte[readBytes];
                        receiveBuf.get(tmp);
                        return tmp;
                    } else {
                        System.out.println("接收到数据为空,重新启动连接");
                        return null;
                    }
                }
            } catch (final IOException e) {
                e.printStackTrace();
                System.out.println("接收消息错误:");
            }
        } else {
            System.out.println("端口没有连接");
        }
        return null;
    }

    public boolean isConnected() {
        return socketChannel != null && socketChannel.isConnected();
    }

    private void select() {
        int n = 0;
        try {
            if (selector == null) {
                return;
            }
            n = selector.select(1000);

        } catch (final Exception e) {
            e.printStackTrace();
        }

        // 如果select返回大于0，处理事件  
        if (n > 0) {
            for (final Iterator<SelectionKey> i = selector.selectedKeys()
                    .iterator(); i.hasNext(); ) {
                // 得到下一个Key  
                final SelectionKey sk = i.next();
                i.remove();
                // 检查其是否还有效  
                if (!sk.isValid()) {
                    continue;
                }

                // 处理事件  
                final IEvent handler = (IEvent) sk.attachment();
                try {
                    if (sk.isConnectable()) {
                        handler.connect(sk);
                    } else if (sk.isReadable()) {
                        handler.read(sk);
                    } else {
                        // System.err.println("Ooops");
                    }
                } catch (final Exception e) {
                    e.printStackTrace();
                    handler.error(e);
                    sk.cancel();
                }
            }
        }
    }

    /**
     * 关闭 连接通道
     */
    public void shutdown() {
        try {
            if (isConnected()) {
                try {
                    socketChannel.close();
                    System.out.println("端口关闭成功");
                } catch (final IOException e) {
                    System.out.println("端口关闭错误:");
                } finally {
                    socketChannel = null;
                    shutdown = true;
                }
            } else {
                System.out.println("通道为空或者没有连接");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    @Override
    public void run() {
        // 启动主循环流程  
        while (!shutdown) {
            try {
                if (isConnected() && (!isSended)) {
                    switch (sysStatus) {
                        case init:
                            doOption();
                            break;
                        case options:
                            doDescribe();
                            break;
                        case describe:
                            doSetup();
                            break;
                        case setup:
                            if (sessionid == null && sessionid.length() > 0) {
                                System.out.println("setup还没有正常返回");
                            } else {
//                                如果是udp连接，建立socket连接通道
                                new Thread(() -> {
                                    try {
                                        UDPReceiver.receive(16264, this);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }).start();
                                doPlay();
                                Thread.sleep(5000);
                                doTeardown();
                            }
                            break;
                        case play:
//                            doPause();
//                            doTeardown();

                            break;

                        case pause:
                            doTeardown();
                            break;
                        default:
                            break;
                    }
                }
                // do select
//                处理服务器发送过来的信息
                select();
                try {
                    Thread.sleep(1000);
                } catch (final Exception e) {
                    e.printStackTrace();
                }
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 建立连接，未连上一直尝试重连
     *
     * @param key
     * @throws IOException
     */
    public void connect(SelectionKey key) throws IOException {
        if (isConnected()) {
            return;
        }
        // 完成SocketChannel的连接  
        socketChannel.finishConnect();
        while (!socketChannel.isConnected()) {
            try {
                Thread.sleep(300);
            } catch (final InterruptedException e) {
                e.printStackTrace();
            }
            socketChannel.finishConnect();
        }

    }

    public void error(Exception e) {
        e.printStackTrace();
    }

    public void read(SelectionKey key) throws IOException {
        // 接收消息  
        final byte[] msg = recieve();
        if (msg != null) {
            handle(msg);
        } else {
            key.cancel();
            shutdown();
        }
    }

    private void handle(byte[] msg) {
        String tmp = new String(msg);
        System.out.println("返回内容：");
        System.out.println(tmp);
        if (tmp.startsWith(RTSP_OK)) {
            switch (sysStatus) {
                case init:
                    sysStatus = Status.options;
                    break;
                case options:
                    sysStatus = Status.describe;
                    trackInfo = tmp.substring(tmp.indexOf("trackID"));
                    break;
                case describe:
                    sessionid = tmp.substring(tmp.indexOf("Session: ") + 9, tmp
                            .indexOf("Date:"));
                    if (sessionid != null && sessionid.length() > 0) {
                        sysStatus = Status.setup;
                    }
                    break;
                case setup:
                    sysStatus = Status.play;
                    break;
                case play:
                    System.out.println("playplayplayplayplay");
                    shutdown();
                    sysStatus = Status.teardown;
//                    sysStatus = Status.pause;
                    break;
                case pause:
                    sysStatus = Status.teardown;
                    shutdown = true;
                    break;
//                case teardown:
//                    sysStatus = Status.init;
//                    break;
                default:
                    break;
            }
            isSended = false;
        } else if (tmp.startsWith(RTSP_UNAUTHORIZED)) {
            doDescribe(tmp);
        } else {
            System.out.println("返回错误：" + tmp);
        }

    }

    private void doTeardown() {
        StringBuilder sb = new StringBuilder();
        sb.append("TEARDOWN ");
        sb.append("rtsp://");
        sb.append(this.address.substring(this.address.indexOf("@") + 1));
        sb.append("/");
        sb.append(VERSION);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append(USER_AGENT);
        sb.append("\r\n");
        sb.append("Session: ");
        sb.append(sessionid);
        sb.append("\r\n");
        send(sb.toString().getBytes());
        System.out.println(sb.toString());
    }

    private void doPlay() {
        StringBuilder sb = new StringBuilder();
        sb.append("PLAY ");
        sb.append(this.address);
        sb.append(VERSION);
        sb.append("Session: ");
        sb.append(sessionid);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append("\r\n");
        System.out.println(sb.toString());
        send(sb.toString().getBytes());

    }

    private void doSetup() {
//        StringBuilder sb = new StringBuilder();
//        sb.append("SETUP ");
//        sb.append("rtsp://");
//        sb.append(this.address.substring(this.address.indexOf("@") + 1));
//        sb.append("/");
//        sb.append(trackInfo);
//        sb.append(VERSION);
//        sb.append("Cseq: ");
//        sb.append(seq++);
//        sb.append("\r\n");
//        sb.append(this.authorization);
//        sb.append(USER_AGENT);
//        sb.append("\r\n");
////        udp连接
//        sb.append("Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play");
////        tcp连接
////        sb.append("Transport: RTP/AVP/TCP;UNICAST;mode=play\r\n");
////        sb.append("Transport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n");
//        sb.append("\r\n");
//        System.out.println(sb.toString());
//        send(sb.toString().getBytes());


        StringBuilder sb = new StringBuilder();
        sb.append("SETUP ");
        sb.append(this.address);
        sb.append("/");
        sb.append(trackInfo);
        sb.append(VERSION);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append("Transport: RTP/AVP;UNICAST;client_port=16264-16265;mode=play\r\n");
        sb.append("\r\n");
        System.out.println(sb.toString());
        send(sb.toString().getBytes());


    }

    private void doOption() {
//        OPTIONS rtsp://172.16.11.172:554/h264/ch1/main/av_stream RTSP/1.0\r\n
//        CSeq: 2\r\n
        StringBuilder sb = new StringBuilder();
        sb.append("OPTIONS ");
        sb.append("rtsp://");
        sb.append(this.address.substring(this.address.indexOf("@") + 1));
        sb.append(VERSION);
        sb.append("CSeq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append(USER_AGENT);
        sb.append("\r\n");
        sb.append("\r\n");
        System.out.println(sb.toString());
        send(sb.toString().getBytes());
    }

    private void doDescribe() {
        StringBuilder sb = new StringBuilder();
        sb.append("DESCRIBE ");
        sb.append("rtsp://");
        sb.append(this.address.substring(this.address.indexOf("@") + 1));
        sb.append(VERSION);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append(USER_AGENT);
        sb.append("\r\n");
        sb.append("Accept: application/sdp");
        sb.append("\r\n");
        sb.append("\r\n");
        System.out.println(sb.toString());
        send(sb.toString().getBytes());
    }

    private void doDescribe(String resp) {
        StringBuilder sb = new StringBuilder();
        sb.append("DESCRIBE ");
        sb.append("rtsp://");
        sb.append(this.address.substring(this.address.indexOf("@") + 1));
        sb.append(VERSION);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        genAuthorization(resp);
        sb.append(this.authorization);
        sb.append(USER_AGENT);
        sb.append("\r\n");
        sb.append("Accept: application/sdp");
        sb.append("\r\n");
        sb.append("\r\n");
        System.out.println(sb.toString());
        send(sb.toString().getBytes());
    }

    private void doPause() {
        StringBuilder sb = new StringBuilder();
        sb.append("PAUSE ");
        sb.append(this.address);
        sb.append("/");
        sb.append(VERSION);
        sb.append("Cseq: ");
        sb.append(seq++);
        sb.append("\r\n");
        sb.append("Session: ");
        sb.append(sessionid);
        sb.append("\r\n");
        send(sb.toString().getBytes());
        System.out.println(sb.toString());
    }

    private void genAuthorization(String resp) {
//        Authorization: Digest username="admin", realm="1868cb021d93", nonce="7a0c8d27a93d3b0f6315b482504aadb3", uri="rtsp://172.16.11.172:554/h264/ch1/main/av_stream", response="7f2362921d8ad19d2bf28bad478921e2"\r\n
        String[] splitRealm = resp.split("Digest realm=\"");
        String splitRealm1 = splitRealm[1];
        String realm = splitRealm1.substring(0, splitRealm1.indexOf("\""));
        String[] splitNonce = resp.split("nonce=\"");
        String splitNonce1 = splitNonce[1];
        String nonce = splitNonce1.substring(0, splitNonce1.indexOf("\""));
        String url = "rtsp://" + this.address.substring(this.address.indexOf("@") + 1);
        String response = genAuthorizationResponse(realm, nonce, url);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Authorization: Digest username=\"");
        stringBuilder.append(this.userName);
        stringBuilder.append("\", realm=\"");
        stringBuilder.append(realm);
        stringBuilder.append("\", nonce=\"");
        stringBuilder.append(nonce);
        stringBuilder.append("\", uri=\"");
        stringBuilder.append(url);
        stringBuilder.append("\", response=\"");
        stringBuilder.append(response);
        stringBuilder.append("\"\r\n");
        this.authorization = stringBuilder.toString();
    }

    /**
     * 计算生成鉴权字符串中的Response
     *
     * @param realm
     * @param nonce
     * @param url
     * @return
     */
    private String genAuthorizationResponse(String realm, String nonce, String url) {
        String response = this.userName + ":" + realm + ":" + this.password;
        response = Md5Utils.md5Encryption(response);
        response = response + ":" + nonce + ":" + Md5Utils.md5Encryption("DESCRIBE:" + url);
        return Md5Utils.md5Encryption(response);
    }


    public static void main(String[] args) {
//        RTSPClient client = new RTSPClient(
////                    new InetSocketAddress("218.207.101.236", 554),
////                    new InetSocketAddress("192.168.2.28", 0),
////                    "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp");
//                new InetSocketAddress("172.16.11.172", 554),
//                new InetSocketAddress("172.16.11.210", 0),
//                "rtsp://admin:ks123456@172.16.11.172:554/h264/ch1/main/av_stream",
//                "admin", "ks123456");
//        genAuthorizationResponse(,,);

/*
        try {
            // RTSPClient(InetSocketAddress remoteAddress,
            // InetSocketAddress localAddress, String address)
            RTSPClient client = new RTSPClient(
//                    new InetSocketAddress("218.207.101.236", 554),
//                    new InetSocketAddress("192.168.2.28", 0),
//                    "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp");
                    new InetSocketAddress("170.23.10.11", 554),
                    new InetSocketAddress("172.16.11.210", 0),
                    "rtsp://admin:lgyqlksga1207@170.16.2.135/ch33/main/av_stream",
                    "admin", "lgyqlksga1207");
            client.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("main方法结束了");
        */

//        System.out.println(CLibrary.INSTANCE.avformat_version());


        try {
            aaa aaa=new aaa();
            aaa.devideByCommand("rtsp://admin:ks123456@172.16.11.172:554/h264/ch1/main/av_stream",null,null);


        } catch (IOException e) {
            e.printStackTrace();
        }



//        bbb bbb=new bbb();
//        try {
//            bbb.changeAmrToMp3(null, null);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


    }

}